﻿using BepInEx.Logging;
using CheatMenu.Managers;
using CheatMenu.Utils;
using Fungus;
using System;
using System.Collections.Generic;
using UnityEngine;
using Logger = BepInEx.Logger;

namespace CheatMenu.Menus
{
    public sealed class MenuStateViewer : MenuBase
    {
        private GUILayoutOption[] stateViewerTypeWidth;
        private GUILayoutOption[] stateViewerEditWidth;
        private GUILayoutOption[] stateViewerNameWidth;
        private GUILayoutOption[] stateViewerValueWidth;

        private GUIStyle defaultFontControlHeightButtonStyle;
        private GUIStyle defaultFontBoxStyle;
        private GUIStyle defaultFontLabelStyle;
        private GUIStyle noPaddingFontBoxStyle;
        private GUIStyle stretchHeightBoxStyle;
        private GUIStyle stretchWidthHeightBoxStyle;
        private GUIStyle labelHeaderStyle;
        private GUIStyle labelTypeStyle;
        private GUIStyle labelEditStyle;
        private GUIStyle labelNameStyle;
        private GUIStyle labelValueStyle;
        private GUIStyle smallButtonStyle;
        private GUIStyle doubleButtonStyle;
        private GUIStyle filterTextFieldStyle;

        private string filterText = "";
        private Vector2 scrollStateViewVector;
        private MenuTextEditor menuTextEditor;

        public MenuStateViewer() : base("StateViewer", null)
        {
            Alignment = Alignment.TopLeft;

            menuTextEditor = new MenuTextEditor("Text Editor");
            menuTextEditor.ButtonClicked += (s, b) =>
            {
                if (b == DialogResult.Apply)
                {
                    if(menuTextEditor.Apply())
                    {
                        MenuManager.GetMenu<MenuNotifications>().PushNotification("Applied value", $"{menuTextEditor.Variable.Key}={menuTextEditor.Result}");
                    }
                    else
                    {
                        MenuManager.GetMenu<MenuNotifications>().PushNotification("Failed to apply value", $"{menuTextEditor.Variable.Key}={menuTextEditor.Result}");
                    }
                }
            };
        }

        public void ResetScrollVector() => scrollStateViewVector = new Vector2();

        protected override Vector2 InitializeSize()
        => new Vector2(GUIManager.ControlWidth + (GUIManager.PaddingSize * 2), -1);

        protected override void OnHidden() => menuTextEditor.Shown = false;

        protected override void InitializeGUI()
        {
            stateViewerTypeWidth = new GUILayoutOption[] { GUILayout.Width((GUIManager.ControlHeight * 2) + GUIManager.PaddingSize), GUILayout.MaxWidth((GUIManager.ControlHeight * 2) + GUIManager.PaddingSize) };
            stateViewerEditWidth = new GUILayoutOption[] { GUILayout.Width(GUIManager.ControlHeight * 2), GUILayout.MaxWidth(GUIManager.ControlHeight * 2) };
            stateViewerNameWidth = new GUILayoutOption[] { GUILayout.Width(GUIManager.ControlWidth), GUILayout.MaxWidth(GUIManager.ControlWidth) };
            stateViewerValueWidth = new GUILayoutOption[] { GUILayout.Width(GUIManager.ControlWidth), GUILayout.MaxWidth(GUIManager.ControlWidth) };

            defaultFontControlHeightButtonStyle = new GUIStyle(GUI.skin.button)
            {
                fontSize = GUIManager.ControlFontSize,
                fixedHeight = GUIManager.ControlHeight
            };

            defaultFontBoxStyle = new GUIStyle(GUI.skin.box)
            {
                fontSize = GUIManager.ControlFontSize
            };

            defaultFontLabelStyle = new GUIStyle(GUI.skin.label)
            {
                fixedHeight = GUIManager.ControlHeight,
                fontSize = GUIManager.ControlFontSize,
                normal = new GUIStyleState
                {
                    textColor = GUIManager.LabelColor
                }
            };

            noPaddingFontBoxStyle = new GUIStyle(GUI.skin.box)
            {
                fontSize = GUIManager.ControlFontSize,
                margin = new RectOffset(0, 0, 0, 0),
                padding = new RectOffset(0, 0, 0, 0),
            };

            stretchHeightBoxStyle = new GUIStyle(GUI.skin.box)
            {
                stretchHeight = true,
            };

            stretchWidthHeightBoxStyle = new GUIStyle(GUI.skin.box)
            {
                stretchWidth = true,
                stretchHeight = true,
            };

            labelTypeStyle = new GUIStyle(GUI.skin.label)
            {
                fixedHeight = GUIManager.ControlHeight,
                fixedWidth = GUIManager.ControlHeight * 2,
                margin = new RectOffset(0, 0, 0, 0),
                padding = new RectOffset(0, 0, 0, 0),
                fontSize = GUIManager.ControlFontSize,
                normal = new GUIStyleState
                {
                    textColor = GUIManager.LabelColor
                }
            };

            labelEditStyle = new GUIStyle(GUI.skin.label)
            {
                fixedHeight = GUIManager.ControlHeight,
                fixedWidth = GUIManager.ControlHeight * 2,
                margin = new RectOffset(0, 0, 0, 0),
                padding = new RectOffset(0, 0, 0, 0),
                fontSize = GUIManager.ControlFontSize,
                normal = new GUIStyleState
                {
                    textColor = GUIManager.LabelColor
                }
            };

            labelNameStyle = new GUIStyle(GUI.skin.label)
            {
                fixedHeight = GUIManager.ControlHeight,
                fixedWidth = GUIManager.ControlWidth,
                margin = new RectOffset(0, 0, 0, 0),
                padding = new RectOffset(0, 0, 0, 0),
                fontSize = GUIManager.ControlFontSize,
                normal = new GUIStyleState
                {
                    textColor = GUIManager.LabelColor
                }
            };

            labelValueStyle = new GUIStyle(GUI.skin.label)
            {
                fixedHeight = GUIManager.ControlHeight,
                margin = new RectOffset(0, 0, 0, 0),
                padding = new RectOffset(0, 0, 0, 0),
                fontSize = GUIManager.ControlFontSize,
                normal = new GUIStyleState
                {
                    textColor = GUIManager.LabelColor
                }
            };

            labelHeaderStyle = new GUIStyle(GUI.skin.label)
            {
                fixedHeight = GUIManager.ControlHeight,
                margin = new RectOffset(0, 0, 0, 0),
                padding = new RectOffset(0, 0, 0, 0),
                fontStyle = FontStyle.Bold,
                fontSize = GUIManager.ControlFontSize,
                normal = new GUIStyleState
                {
                    textColor = GUIManager.LabelHeaderColor
                }
            };

            smallButtonStyle = new GUIStyle(GUI.skin.button)
            {
                fixedHeight = GUIManager.ControlHeight,
                fixedWidth = GUIManager.ControlHeight,
                margin = new RectOffset(0, 0, 0, 0),
                padding = new RectOffset(0, 0, 0, 0),
                fontSize = GUIManager.ControlFontSize
            };

            doubleButtonStyle = new GUIStyle(GUI.skin.button)
            {
                fixedHeight = GUIManager.ControlHeight,
                fixedWidth = GUIManager.ControlHeight * 2,
                margin = new RectOffset(0, 0, 0, 0),
                padding = new RectOffset(0, 0, 0, 0),
                fontSize = GUIManager.ControlFontSize
            };

            filterTextFieldStyle = new GUIStyle(GUI.skin.textField)
            {
                fixedHeight = GUIManager.ControlHeight,
                fixedWidth = GUIManager.ControlWidth,
                fontSize = GUIManager.ControlFontSize
            };
        }

        protected override void MenuWindow(int id)
        {
            GUILayout.BeginVertical();
            {
                GUILayout.BeginHorizontal();
                {
                    GUILayout.BeginHorizontal(GUI.skin.box);
                    {
                        GUILayout.Label("Filter:", defaultFontLabelStyle);
                        filterText = GUILayout.TextField(filterText, filterTextFieldStyle);
                        if (GUILayout.Button("Clear", defaultFontControlHeightButtonStyle)) { filterText = ""; }
                    }
                    GUILayout.EndHorizontal();

                    GUILayout.BeginHorizontal(GUI.skin.box);
                    {
                        if (GUILayout.Button("Close", defaultFontControlHeightButtonStyle)) { Shown = false; }
                    }
                    GUILayout.EndHorizontal();
                }
                GUILayout.EndHorizontal();

                GUILayout.BeginVertical(GUI.skin.box);
                {
                    GUILayout.BeginHorizontal();
                    {
                        GUILayout.Space(1);
                        GUILayout.Label("Type", noPaddingFontBoxStyle, stateViewerTypeWidth);
                        GUILayout.Space(1);
                        GUILayout.Label("Edit", noPaddingFontBoxStyle, stateViewerEditWidth);
                        GUILayout.Space(1);
                        GUILayout.Label("Name", noPaddingFontBoxStyle, stateViewerNameWidth);
                        GUILayout.Space(1);
                        GUILayout.Label("Value", noPaddingFontBoxStyle, stateViewerValueWidth);
                    }
                    GUILayout.EndHorizontal();

                    GUILayout.BeginVertical(stretchHeightBoxStyle);
                    {
                        List<FilteredFlowchart> filtered = StateManager.GetFilteredFlowcharts(filterText);

                        scrollStateViewVector = GUILayout.BeginScrollView(scrollStateViewVector, false, false, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, stretchWidthHeightBoxStyle);
                        for (int i = 0; i < filtered.Count; i++)
                            DrawStateChart(filtered[i]);

                        GUILayout.EndScrollView();

                        //Hacky workaround for the ScrollView not taking mouse scrollwheel focus
                        if (GUIUtil.MouseScrollingAndContained(WindowRect))
                            Input.ResetInputAxes();
                    }
                    GUILayout.EndVertical();
                }
                GUILayout.EndVertical();
            }
            GUILayout.EndVertical();
        }

        private void DrawStateChart(FilteredFlowchart filtered)
        {
            GUILayout.Label(string.Format("FC:{0}", filtered.Flowchart.name), labelHeaderStyle);
            for (int i = 0; i < filtered.Variables.Count; i++)
                DrawStateEntry(filtered.Flowchart, filtered.Variables[i]);
        }

        private void DrawStateEntry(Flowchart chart, Variable variable)
        {
            Type type = variable.GetType();
            if (type == typeof(BooleanVariable))
            {
                BooleanVariable booleanVariable = (BooleanVariable)variable;
                GUILayout.BeginHorizontal();
                {
                    GUILayout.Label("Bool", labelTypeStyle);
                    if (GUILayout.Button(booleanVariable.Value ? "true" : "false", doubleButtonStyle))
                    {
                        booleanVariable.Value = !booleanVariable.Value;
                        //chart.SetBooleanVariable(variable.Key, booleanVariable.Value);
                        PlayerPrefs.SetInt(SetSaveProfile.SaveProfile + "_" + chart.SubstituteVariables(variable.Key), booleanVariable.Value ? 1 : 0);
                    }
                    GUILayout.Space(1);
                    GUILayout.Label(variable.Key, labelNameStyle);
                    GUILayout.Space(1);
                    GUILayout.Label(booleanVariable.Value.ToString(), labelValueStyle, GUILayout.ExpandWidth(true));
                }
                GUILayout.EndHorizontal();
                return;
            }
            if (type == typeof(IntegerVariable))
            {
                IntegerVariable integerVariable = (IntegerVariable)variable;
                GUILayout.BeginHorizontal();
                {
                    GUILayout.Label("Int", labelTypeStyle);
                    bool hitPlus = GUILayout.Button("+", smallButtonStyle);
                    bool hitMinus = GUILayout.Button("-", smallButtonStyle);
                    if (hitPlus || hitMinus)
                    {
                        int addValue = GetIntFromModifierKeys(hitPlus);
                        Logger.Log(LogLevel.Info, $"Add/Subtract To Int: {addValue}");
                        integerVariable.Value += addValue;
                        //chart.SetIntegerVariable(variable.Key, integerVariable.Value);
                        PlayerPrefs.SetInt(SetSaveProfile.SaveProfile + "_" + chart.SubstituteVariables(variable.Key), integerVariable.Value);
                    }
                    GUILayout.Space(1);
                    GUILayout.Label(variable.Key, labelNameStyle);
                    GUILayout.Space(1);
                    GUILayout.Label(integerVariable.Value.ToString(), labelValueStyle, GUILayout.ExpandWidth(true));
                }
                GUILayout.EndHorizontal();
                return;
            }
            if (type == typeof(StringVariable))
            {
                StringVariable stringVariable = (StringVariable)variable;
                GUILayout.BeginHorizontal();
                {
                    GUILayout.Label("Str", labelTypeStyle);
                    if (GUILayout.Button("Edit", doubleButtonStyle))
                    {
                        menuTextEditor.Populate(chart, stringVariable);
                        menuTextEditor.Shown = true;
                    }
                    GUILayout.Space(1);
                    GUILayout.Label(variable.Key, labelNameStyle);
                    GUILayout.Space(1);
                    GUILayout.Label(stringVariable.Value, labelValueStyle, GUILayout.ExpandWidth(true));
                }
                GUILayout.EndHorizontal();
                return;
            }
            if (type == typeof(FloatVariable))
            {
                FloatVariable floatVariable = (FloatVariable)variable;
                GUILayout.BeginHorizontal();
                {
                    GUILayout.Label("Float", labelTypeStyle);
                    bool hitPlus = GUILayout.Button("+", smallButtonStyle);
                    bool hitMinus = GUILayout.Button("-", smallButtonStyle);
                    if (hitPlus || hitMinus)
                    {
                        floatVariable.Value += GetFloatFromModifierKeys(hitPlus);
                        //chart.SetFloatVariable(variable.Key, floatVariable.Value);
                        PlayerPrefs.SetFloat(SetSaveProfile.SaveProfile + "_" + chart.SubstituteVariables(variable.Key), floatVariable.Value);
                    }
                    GUILayout.Space(1);
                    GUILayout.Label(variable.Key, labelNameStyle);
                    GUILayout.Space(1);
                    GUILayout.Label($"{floatVariable.Value:0.00}", labelValueStyle, GUILayout.ExpandWidth(true));
                }
                GUILayout.EndHorizontal();
                return;
            }
            GUILayout.BeginHorizontal();
            {
                GUILayout.Label($"{type.Name.Replace("Variable", "")}", labelTypeStyle);
                GUILayout.Label(GUIContent.none, labelEditStyle);//Placeholder
                GUILayout.Space(1);
                GUILayout.Label(variable.Key, labelNameStyle);
                GUILayout.Space(1);
                GUILayout.Label(variable.ToString(), labelValueStyle, GUILayout.ExpandWidth(true));
            }
            GUILayout.EndHorizontal();
        }

        private int GetIntFromModifierKeys(bool positive)
        {
            bool shiftDown = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
            bool ctrlDown = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
            int result = shiftDown ? (ctrlDown ? 100 : 10) : 1;
            return positive ? result : -result;
        }

        private float GetFloatFromModifierKeys(bool positive)
        {
            bool shiftDown = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
            bool ctrlDown = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
            float result = shiftDown ? (ctrlDown ? 1f : 0.1f) : 0.05f;
            return positive ? result : -result;
        }
    }
}